/*
    jQuery plugin for AJAX-based page transition.
    Copyright (c) 2010 Keiji Hokamura (famteam)
    This plugin available for use in all personal or commercial projects under both MIT and GPL licenses.
 */

(function($) {
	var methods = {
		setup: function(readyHandler, loadHandler, appearHandler, disappearHandler, pageClass, links, defaultPage, self) {
			var linksMap = {};
			if (links != null) {
				links.each(function(index) {
					var anchor = $(this);
					
					anchor.click(function(event) {
						event.preventDefault();
						self.pager({
							action: $.pager.ACTION_TRANSITION,
							url: anchor.attr('href')
						});
					});
					
					var hashURL = $.pager._hashURL(anchor.attr('href'));
					if (linksMap[hashURL] == null) {
						linksMap[hashURL] = [];
					}
					linksMap[hashURL].push(this);
				});
			}
		
			return self.data($.pager.DATA, {
				map: {},
				readyHandler: readyHandler,
				loadHandler: loadHandler,
				appearHandler: appearHandler,
				disappearHandler: disappearHandler,
				pageClass: pageClass,
				linksMap: linksMap,
				defaultPage: defaultPage,
				toURL: null
			});
		},
		add: function(url, preload, readyHandler, loadHandler, appearHandler, disappearHandler, self) {
			if (url != null) {
				var data = self.data($.pager.DATA);
				var map = data != null ? data.map : null;
				hashURL = $.pager._hashURL(url);
				if (map != null && map[hashURL] == null) {
					map[hashURL] = {
						url: url,
						element: null,
						original: null,
						startedLoad: false,
						loaded: false,
						readyHandler: readyHandler,
						loadHandler: loadHandler,
						appearHandler: appearHandler,
						disappearHandler: disappearHandler
					};
					if (url.substr(url.lastIndexOf('.html')) != '.html' && $('#' + url).size() > 0) {
						map[hashURL].loaded = true;
						map[hashURL].element = $('#' + url).hide();
					}
					else if (preload) {
						$.pager._load(url, $.pager._preloadHandler, self);
					}
					if (data.defaultPage == null) {
						data.defaultPage = url;
					}
				}
			}
			return self;
		},
		transition: function(url, back, self) {
			var data = self.data($.pager.DATA);
			if (data != null) {
				var map = data.map;
				var hashURL = $.pager._hashURL(url);
				if (url != data.previousURL && map != null && map[hashURL] != null) {
					var previousHashURL = $.pager._hashURL(data.previousURL);
//					alert('Transition: ' + previousHashURL + '> ' + hashURL);
					data.toURL = url;
					var disappearHandler = null;
					if (previousHashURL != null && map[previousHashURL] != null) {
						disappearHandler = map[previousHashURL].disappearHandler != null ? map[previousHashURL].disappearHandler : data.disappearHandler;						
					}
					var appearHandler = map[hashURL].appearHandler != null ? map[hashURL].appearHandler : data.appearHandler;
					function pageTransition() {
						var from = {};
						if (previousHashURL != null && map[previousHashURL] != null) {
							from = {
								url: map[previousHashURL].url,
								element: map[previousHashURL].element,
								links: data.linksMap[previousHashURL]
							};
						}
						var transitionInformation = {
							from: from,
							to: {
								url: map[hashURL].url,
								element: map[hashURL].element,
								links: data.linksMap[hashURL]
							},
							back: back
						};
						if (disappearHandler != null) {
							transitionInformation.appearChain = function() {
								window.location.hash = hashURL;
								// For IE title bug related to hash URL
								if ($.browser.msie) {
									window.document.title = $.pager.BASE_TITLE;
								}
								
								transitionInformation.appearChain = null;
								appearHandler(transitionInformation);
							};
							disappearHandler(transitionInformation);
						}
						else {
							window.location.hash = hashURL;
							// For IE title bug related to hash URL
							if ($.browser.msie) {
								window.document.title = $.pager.BASE_TITLE;
							}

							appearHandler(transitionInformation);
						}
						data.previousURL = url;
					}
					if (map[hashURL].loaded) {
						pageTransition();
					}
					else if (!map[hashURL].startedLoad) {
						$.pager._load(url, pageTransition, self);
					}
				}
			}
			return self;
		},
		initialize: function(self) {
			var data = self.data($.pager.DATA);
			if (data != null && data.map != null) {
				var defaultPage = null;
				if (window.location.hash != '') {
					var toHashURL = window.location.hash;
					window.location.hash = '';
					for (var hashURL in data.map) {
						if (hashURL == toHashURL) {
							defaultPage = $.pager._originalURL(toHashURL);
							if (defaultPage != data.defaultPage) {
								data.previousURL = data.defaultPage;
							}
						}
					}
				}
				if (defaultPage == null) {
					defaultPage = data.defaultPage;
				}
				self.pager({
					action: $.pager.ACTION_TRANSITION,
					url: defaultPage
				});
				
				$.hashMonitor.start(function(previousHash) {
					self.pager({
						action: $.pager.ACTION_TRANSITION,
						url: window.location.hash.substring(1),
						back: true
					});
				});
			}
			return self;
		}
	};
	
	$.fn.pager = function(settings) {
		settings = $.extend( {
			action: $.pager.ACTION_SETUP,
			// For setup
			pageClass: '.page',
			links: null,
			defaultPage: null,
			// For setup and add
			loadHandler: null,
			readyHandler: null,
			appearHandler: null,
			disappearHandler: null,
			// For add
			preload: false,
			// For add and transition
			url: null,
			// For transition
			back: false
		}, settings);
		
		switch (settings.action) {
			case $.pager.ACTION_SETUP:
				return methods.setup(settings.readyHandler, settings.loadHandler, settings.appearHandler, settings.disappearHandler, settings.pageClass, settings.links, settings.defaultPage, this);
			case $.pager.ACTION_ADD:
				return methods.add(settings.url, settings.preload, settings.readyHandler, settings.loadHandler, settings.appearHandler, settings.disappearHandler, this);
			case $.pager.ACTION_TRANSITION:
				return methods.transition(settings.url, settings.back, this);
			case $.pager.ACTION_INITIALIZE:
				return methods.initialize(this);
		}
	};

	$.pager = {
		DATA: 'PAGER',
		ACTION_SETUP: 'setup',
		ACTION_ADD: 'add',
		ACTION_TRANSITION: 'transition',
		ACTION_INITIALIZE: 'initialize',
		
		_hashURL: function(url) {
			return '#' + url;
		},
		_originalURL: function(hashURL) {
			return hashURL.substring(1);
		},
		_preloadHandler: function(url, element) {
			var self = element.parent();
			var data = self.data($.pager.DATA);
//			alert('Preloaded: ' + url + ' (' + data.toURL + ')');
			if (data != null && data.toURL == url) {
//				alert('Transition: ' + url + ' (' + data.toURL + ')');
				self.pager({
					action: $.pager.ACTION_TRANSITION,
					url: url
				});
			}
		},
		_load: function(url, additionalLoadHandler, self) {
			var hashURL = $.pager._hashURL(url);
			var data = self.data($.pager.DATA);
			var map = data.map;
			map[hashURL].startedLoad = true;
			$.ajax({
				url: url,
				success: function(response) {
					map[hashURL].original = response;
					map[hashURL].element = $(response).find(data.pageClass);

					var readyHandler = map[hashURL].readyHandler != null ? map[hashURL].readyHandler : data.readyHandler;
					if (readyHandler != null) {
						readyHandler(url, map[hashURL].element, map[hashURL].original);
					}
					
					map[hashURL].element.hide().appendTo(self);
					
					var images = map[hashURL].element.find('img');
					var imageTotal = images.size();
					var loadImageNumber = 0;
					function imageLoadHandler(event) {
//						alert(hashURL + ': ' + (loadImageNumber + 1) + ': ' + imageTotal);
//						if (event.type == 'error') {
//							alert('Error: ' + event.target.src + " (" + hashURL + ')');
//						}
						if (++loadImageNumber >= imageTotal) {
							callLoadHandler();
						}
					}
					function callLoadHandler() {
						map[hashURL].loaded = true;

						var loadHandler = map[hashURL].loadHandler != null ? map[hashURL].loadHandler : data.loadHandler;
						if (loadHandler != null) {
							loadHandler(url, map[hashURL].element, map[hashURL].original);
						}
						if (additionalLoadHandler != null) {
							additionalLoadHandler(url, map[hashURL].element, map[hashURL].original);
						}
					}
					if (imageTotal > 0) {
						images.each(function() {
							var image = $(this);
							$('<img src="' + image.attr('src') + '" />').load(imageLoadHandler).error(imageLoadHandler);
						});
					}
					else {
						callLoadHandler();
					}
				},
				dataType: 'text'
			});
		}
	};
})(jQuery);